package com.espirit.eap.sa;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.espirit.eap.conversion.PageConverterService;
import com.espirit.eap.manager.I18n;
import com.espirit.eap.manager.I18nService;
import com.espirit.eap.manager.workflow.WorkflowServiceAware;
import com.espirit.eap.pagelayout.AvabButton;
import com.espirit.eap.pagelayout.LinkedButton;
import com.espirit.eap.pagelayout.Model;
import com.espirit.eap.pagelayout.ModelField;
import com.espirit.eap.pagelayout.ModelService;
import com.espirit.eap.util.Functions;
import com.espirit.eap.util.TaglibManager;
import com.googlecode.cswish.annotation.GenericType;
import com.googlecode.cswish.annotation.ParamScope;
import com.googlecode.cswish.annotation.Protected;
import com.googlecode.cswish.annotation.Rest;
import com.googlecode.cswish.annotation.ScopeValue;
import com.googlecode.cswish.exception.ApplicationException;
import com.googlecode.cswish.model.KeyValue;
import com.googlecode.cswish.model.PageElement;
import com.googlecode.cswish.model.PageElements;
import com.googlecode.cswish.model.PageInfo;
import com.googlecode.cswish.model.Reference;
import com.googlecode.cswish.oosearch.Condition;
import com.googlecode.cswish.oosearch.QLInfo;
import com.googlecode.cswish.security.SafeManager;
import com.googlecode.cswish.struts.ContextHolder;
import com.googlecode.cswish.struts.Invoker;
import com.googlecode.cswish.struts.Routing;
import com.googlecode.cswish.struts.conversion.PageElementService;
import com.googlecode.cswish.struts.spring.BeanIntrospector;
import com.googlecode.cswish.struts.spring.GenericService;
import com.googlecode.cswish.struts.spring.ObjectFactory;
import com.googlecode.cswish.util.ClassUtil;
import com.googlecode.cswish.util.FrameConstant;
import com.opensymphony.xwork2.ActionContext;
import com.thoughtworks.xstream.annotations.Annotations;

/**
 * 1. Privilege = model + method
 * 2. method includes standard method and non-standard method
 * 3. method has the method group (method mapping) concept, a group of privilege is the minimum permission unit
 * 4. the privilege interface can select the minimum permission unit by clicking the round icon
 * 
 * @author Jerry
 *
 */
@Service
public class PrivilegeService {

	private final static Logger logger = Logger.getLogger(PrivilegeService.class);
	
	@Resource
	private GenericService genericService;
	
	@Resource
	private PageElementService pageElementService;
	
	@Resource
	private I18nService i18nService;
		
	@Resource
	private LoginUserService loginUserService;
	
	@Resource
	private Functions functions;
	
	@Resource
	private BeanIntrospector beanIntrospector;
	
	@Resource
	private Routing routing;
	
	@Resource
	private SafeManager safeManager;
	
	@Resource
	private PageConverterService pageConverterService;
	
	@Resource
	private TaglibManager tagManager;
	
	@Resource
	private WebsiteService websiteService;
	
	@Resource
	private ModelService modelService;
	
	@Resource
	private ObjectFactory objectFactory;
	
	// cache the privilege by product name
	private Map<String, GroupPrivilege> groupPrivileges = new ConcurrentHashMap<String, PrivilegeService.GroupPrivilege>();
	
	// TODO: use @Rest to REPLACE this function, 1. add Rest to service method 2. remove the privilege definition from SaleDBAService.initAllPrivileges
	// Mapping genericService method to the standard CRUD method
	@Deprecated
	private final static Map<String, String> methodMapping = new HashMap<String, String>();
	@Deprecated
	private final static Map<String, Collection<String>> methodMappingGroup = new HashMap<String, Collection<String>>();
	static {
		addMethodMapping("_add", "add");
		addMethodMapping("add", "add");
		addMethodMapping("addElement", "add");					// page
		
		addMethodMapping("report", "report");
		addMethodMapping("print", "report");
		
		addMethodMapping("approve", "_approve");
		addMethodMapping("get", "search");
		addMethodMapping("view", "search");
		addMethodMapping("viewProperty", "search");
		addMethodMapping("load", "search");
		addMethodMapping("applySelectedPrivileges", "search");
		addMethodMapping("search", "search");
		addMethodMapping("_search", "search");
		addMethodMapping("_searchWeb", "search");
		
		addMethodMapping("searchItem", "search");
		addMethodMapping("searchReadyResult", "search");

		addMethodMapping("searchAll", "search");
		addMethodMapping("searchByModel", "search");
		addMethodMapping("searchInDepartment", "search");
		addMethodMapping("applySelectedPrivileges", "search");
		
		addMethodMapping("importFile", "_importFile");
		addMethodMapping("_importFile", "_importFile");
		
		addMethodMapping("export", "export");
		
		addMethodMapping("_update", "update");
		addMethodMapping("update", "update");
		addMethodMapping("_updateList", "update");
		addMethodMapping("updateList", "update");
		addMethodMapping("_merge", "update");
		addMethodMapping("merge", "update");
		addMethodMapping("updateElement", "update");
		
		addMethodMapping("delete", "delete");
		addMethodMapping("deleteList", "delete");

		addMethodMapping("append", "append");
		addMethodMapping("getLayout", "getLayout");
		
		addMethodMapping("switchTargetLayout", "getLayout");
		addMethodMapping("searchInvInfoByPartItem", "search"); // inventoryService
		
		addMethodMapping("searchByNeedToOffItems", "search"); // apItemService
		
		addMethodMapping("searchInDepartment", "search"); // user
		
//		addMethodMapping("searchByNeedToCallItems", "search"); // apItemService
		
		addMethodMapping("_batchAdd", "add");
		
		
		addMethodMapping("_addAvab", "add");
		
//		addMethodMapping("getAllocatedUsers", "search");
		addMethodMapping("searchDatasource", "search");		// page
		addMethodMapping("searchAllocatedSI", "search");
	}
	
	private static void addMethodMapping(String fromMethod, String toMethod) {
		methodMapping.put(fromMethod, toMethod);
		
		Collection<String> fromList = methodMappingGroup.get(toMethod);
		if (fromList == null) {
			fromList = new ArrayList<String>(5);
			methodMappingGroup.put(toMethod, fromList);
		}
		methodMappingGroup.put(fromMethod, fromList);
		fromList.add(fromMethod);
	}
	
	@Deprecated
	private final static Map<String, String> othersMethodMapping = new HashMap<String, String>();
	{
		othersMethodMapping.put("showHomepage", "showHomepage");		// partItem
		othersMethodMapping.put("showDetail", "showHomepage");			// partItem, see ShowDetailAction
		othersMethodMapping.put("showList", "showHomepage");			// partItem
		othersMethodMapping.put("viewFile", "showHomepage");			// partItem
		
//		othersMethodMapping.put("getEventAccountInfo", "getAccountInfo");
		othersMethodMapping.put("_batchOffer", "_generateOffer");
	}
	
	@Deprecated
	public Collection<String> getMethodMappings(String method) {
		return methodMappingGroup.get(method);
	}
	
	
	// cache
	//private GroupPrivilege groupPrivilege;

	
	public void update(Privilege model){
		genericService.update(model);
	}
	
	@Protected
	@Transactional
	public <T extends UserTemplate> T update(T model, List<String> privileges) {
		// clean the invalid userTemplate parameter TODO: put the logic to the front page?
		if (model.getUserTemplate() != null && model.getUserTemplate().getId() == null) {
			model.setUserTemplate(null);
		}
		
		boolean updatePrivilege = privileges != null;
		T user = model;
		
		/*
		 * 1. 删除子记录: 需要在OneToMany中加入orphanRemoval=true
		 * 2. 删除关系: 需要children中remove, 然后children item的parent设为null
		 */
		if (updatePrivilege) {
			// build the new relationship
			Map<Integer, UserTemplatePrivilege> oldPagePermissions = null;
			if (model.getId() != null) {
				UserTemplate dbUser = genericService.load(UserTemplate.class, model.getId());
				oldPagePermissions = new HashMap<Integer, UserTemplatePrivilege>();
				for (UserTemplatePrivilege pagePermission : dbUser.getUserTemplatePrivileges()) {
					if (pagePermission.getPrivilege() != null){
						Integer privilegeId = pagePermission.getPrivilege().getId();
						oldPagePermissions.put(privilegeId, pagePermission);
					}
				}
			}
			
			
			// update userTemplatePrivileges, privilege id list --> userTemplatePrivilege list
			List<UserTemplatePrivilege> userPrivilegeList = new ArrayList<UserTemplatePrivilege>();
			for (String privilege : privileges) {
				if (privilege.isEmpty()) {
					continue;
				}
				
				String[] ids = privilege.split(":");
				
				for (String id : ids) {
					Integer privilegeId = new Integer(id);
					UserTemplatePrivilege userTemplatePrivilege = null;
					if (oldPagePermissions != null) {
						userTemplatePrivilege = oldPagePermissions.get(privilegeId);
					}
					if (userTemplatePrivilege == null) { 
						userTemplatePrivilege = new UserTemplatePrivilege();
					}
					
					userTemplatePrivilege.setUserTemplate(user);
					Privilege privilegeModel = genericService.load(Privilege.class, new Integer(id));
					userTemplatePrivilege.setPrivilege(privilegeModel);
					userPrivilegeList.add(userTemplatePrivilege);
				}
			}
			
			// for the security reason, admin user must has user permission
			boolean keepUserPrivilege = LoginUser.SUPER_ADMIN.equals(model.getName());
			// TODO: 
			
			// keep the other site privilege if user only selects the specified site
			String selectedProductId = null;
			if (model.getDefaultWebsite() != null) {
				selectedProductId = model.getDefaultWebsite().getId();
			}
			if (selectedProductId != null) {
				String selectedProduct = genericService.load(Website.class, selectedProductId).getName();
				
				if (oldPagePermissions != null && !oldPagePermissions.isEmpty()){
					for (UserTemplatePrivilege pagePermission : oldPagePermissions.values()) {
						String productName = pagePermission.getPrivilege().getProductName();
						if (selectedProduct != null && !selectedProduct.equals(productName)) {
							userPrivilegeList.add(pagePermission);
						}
					}
				}
			}
			
			user.setUserTemplatePrivileges(userPrivilegeList);
		}
		
		if (user.getId() != null) {
			// update user base information
			Set<String> enabledFields = new HashSet<String>(Arrays.asList(new String[]{
					"name", "password", "description"
			}));
			
			if (updatePrivilege) {
				enabledFields.add("userTemplatePrivileges");
				user = genericService.update(user, true, enabledFields, null);
			} else {
				user = genericService.update(user, true, enabledFields, null);
			}
		} else {	
			user = genericService.add(user);
		}
		return user;
	}
	
	/**
	 * 
	 * @param userId
	 * @param isTemplate valid when the userId is null
	 * @param templateId
	 * @return
	 */
	@Rest(simpleServiceName="espirit.eap.sa.user", type="add")
	public List<String> applySelectedPrivileges(String userId, String name,
			boolean isTemplate, String templateId, boolean clearOldData
			, @ParamScope(scopeKey="'Privilege.applySelectedPrivileges'", visible=false, scope=ScopeValue.SESSION)
			Reference<UserTemplate> selectedPrivileges) {
		if (templateId == null) {
			I18n i18n = i18nService.getI18n("model.espirit.eap.sa.user.template");
			throw new ApplicationException("errors.requiredSelect", i18n.getName());
		}
		if (name == null) {
			I18n i18n = i18nService.getI18n("model.espirit.eap.sa.user.name");
			throw new ApplicationException("errors.requiredSelect", i18n.getName());
		}
		UserTemplate template = new UserTemplate();
		template.setId(templateId);
		template = genericService.load(template);
		
		UserTemplate user = isTemplate ? new Template() : new LoginUser();
		if (userId == null) {
			user.setName(name);
			//user = genericService.merge(user);
		} else {
			user.setId(userId);
			user = genericService.load(user);
		}
		
		// copy page privilege
		List<UserTemplatePrivilege> copyPagePrivilegeList = user.getUserTemplatePrivileges();
		if (copyPagePrivilegeList != null) {
			if (clearOldData) {
				copyPagePrivilegeList.clear();					// clear old page privilege & page field permission
			}
		} else {
			copyPagePrivilegeList = new ArrayList<UserTemplatePrivilege>(template.getUserTemplatePrivileges().size());
			user.setUserTemplatePrivileges(copyPagePrivilegeList);
		}
		// add new privilege
		for (UserTemplatePrivilege userTemplatePrivilege : template.getUserTemplatePrivileges()) {
			UserTemplatePrivilege copyUserTemplatePrivilege = new UserTemplatePrivilege();
			copyUserTemplatePrivilege.setUserTemplate(user);
			copyUserTemplatePrivilege.setPrivilege(userTemplatePrivilege.getPrivilege());
			copyPagePrivilegeList.add(copyUserTemplatePrivilege);
			
			// copy page field permission
			if (userTemplatePrivilege.getPageFieldPermissions() != null && userTemplatePrivilege.getPageFieldPermissions().size() > 0) {
				List<PageFieldPermission> copyPageFieldPermissionList 
					= new ArrayList<PageFieldPermission>(userTemplatePrivilege.getPageFieldPermissions().size());
				for(FieldPermission fieldPermission : userTemplatePrivilege.getPageFieldPermissions()){
					PageFieldPermission pageFieldPermission = new PageFieldPermission();
					fieldPermission.copyTo(pageFieldPermission);
					pageFieldPermission.setUserTemplatePrivilege(copyUserTemplatePrivilege);
					copyPageFieldPermissionList.add(pageFieldPermission);
				}
				copyUserTemplatePrivilege.setPageFieldPermissions(copyPageFieldPermissionList);
			}
		}
		
		// copy model field permission
		List<ModelFieldPermission> copyModelFieldPermissionList = user.getModelFieldPermissions();
		if (copyModelFieldPermissionList != null) {
			copyModelFieldPermissionList.clear();			// clear old model field permission
		} else {
			copyModelFieldPermissionList = new ArrayList<ModelFieldPermission>(template.getModelFieldPermissions().size());
			user.setModelFieldPermissions(copyModelFieldPermissionList);
		}
		// add the new field permission
		for (ModelFieldPermission modelFieldPermission : template.getModelFieldPermissions()) {
			ModelFieldPermission mfp = new ModelFieldPermission();
			modelFieldPermission.copyTo(mfp);
			mfp.setUserTemplate(user);
			mfp.setModelSimpleName(modelFieldPermission.getModelSimpleName());
			copyModelFieldPermissionList.add(mfp);
		}
		
		// save merged user privilege to db
		//genericService.merge(user);
		selectedPrivileges.set(user);
		
		List<String> privileges = loadSelectedPrivileges(user);
		
		return privileges;
	}
	
	public List<String> loadSelectedPrivileges(String templateId) {
		List<String> selectedPrivileges;
		if (templateId != null) {
			UserTemplate template = new UserTemplate();
			template.setId(templateId);
		
			UserTemplate dbTemplate = genericService.load(template);
			selectedPrivileges = loadSelectedPrivileges(dbTemplate);
		} else {
			selectedPrivileges = new ArrayList<String>(0);
		}
		return selectedPrivileges;
	}
	
	private List<String> loadSelectedPrivileges(UserTemplate template) {
		List<String> selectedPrivileges = new ArrayList<String>();
		List<UserTemplatePrivilege> userTemplatePrivileges = template.getUserTemplatePrivileges();
		for (UserTemplatePrivilege userTemplatePrivilege : userTemplatePrivileges) {
			Privilege privilege = userTemplatePrivilege.getPrivilege();
			if (privilege == null) continue;
			String mappingMethod = methodMapping.get(privilege.getMethod());
			if (mappingMethod == null) {
				mappingMethod = othersMethodMapping.get(privilege.getMethod());
			}
			if (mappingMethod == null) {
				mappingMethod = privilege.getMethod();
			}
			if (mappingMethod == null) {
				mappingMethod = "execute";
			}
			String id = getMappingModelSimpleName(privilege).replace('.', '_') + '_' 
					+ mappingMethod + ':' + privilege.getScopeType().value;
			selectedPrivileges.add(id);
		}
		return selectedPrivileges;
	}
	
	// FIXME: right ?
	private String getMappingModelSimpleName(Privilege privilege) {
		String name = privilege.getMappingModelSimpleName();
		if (name == null) {
			name = privilege.getModelSimpleName();
		}
		return name;
	}
	
	@Rest(simpleServiceName="espirit.eap.sa.user", type="add")
	public void showModelFieldPrivilege(String category, String model, String userId, Integer websiteId,
			List<KeyValue> categoryOptions, List<KeyValue> modelOptions, List<FieldPermission> fieldPermissions) {
		UserTemplate user = genericService.load(UserTemplate.class, userId);
		Website website;
		if (websiteId == null) {
			website = websiteService.getWebsite(ContextHolder.get().getSiteName());
			if (website == null) {
				website = ObjectFactory.getInstance().getBean(LoginUserService.class, false).getCurrentWebsite();
			}
		} else {
			website = genericService.load(Website.class, websiteId);
		}
		
		// category is same with the GroupPrivilege for the better performance
		LoginUser admin = loginUserService.getDefaultAdminUser();					// use admin user to load all the category
		GroupPrivilege groupPrivilege = getGroupPrivilege(admin.getId());
		
		for (String categoryData : groupPrivilege.categories) {
			String i18nName = i18nService.getI18n("module." + categoryData).getName();
			categoryOptions.add(new KeyValue(categoryData, i18nName));
		}
		if (category == null) {
			Collection<String> categories = groupPrivilege.getCategories();
			if (categories.size() > 0) {
				category = categories.iterator().next();
				ActionContext.getContext().getValueStack().setValue("category", category);
			}
		}
		// Model information comes from the Model table
		Model modelExample = new Model();
		modelExample.setName(website.getName() + '.' + category);
		List<Model> data = genericService.searchByModel(modelExample, Condition.SEARCH_LIKEFIRST).getData();
		for (Model modelData : data) {
			String i18nName = modelService.getI18nValue(modelData.getName());
			modelOptions.add(new KeyValue(modelData.getName(), i18nName));
		}
		if (model == null && data.size() > 0) {
			model = data.iterator().next().getName();
			ActionContext.getContext().getValueStack().setValue("model", model);
		}
		
		// load user's field permission
		Map<String, FieldPermission> userFieldPermission = new HashMap<String, FieldPermission>();
		String ql = "from " + ModelFieldPermission.class.getName() + " a where a.userTemplate.id=? and a.modelSimpleName=?";
		QLInfo qlInfo = new QLInfo(ql, true, userId, model);
		List<FieldPermission> modelFieldPermissions = genericService.searchByQl(qlInfo, false).getData();
		
		for (FieldPermission fieldPermission : modelFieldPermissions) {
			userFieldPermission.put(fieldPermission.getFieldName(), fieldPermission);
		}
		
		modelExample.setName(model);
		Model theModel = genericService.searchAll(modelExample).getData(0);
		for (ModelField mf : theModel.getModelFields()) {
			String fieldName = mf.getName();
			
			FieldPermission permission = new FieldPermission();
			permission.setFieldName(fieldName);
			if (mf.getI18n() == null) {
				permission.setFieldNameLabel(fieldName);
			} else {
				permission.setFieldNameLabel(mf.getI18n());
			}
			permission.setEnableFieldPermissionType(FieldPermissionType.READWRITE);
			FieldPermission userPermission = userFieldPermission.get(permission.getFieldName());
			FieldPermissionType filePermissionType; 
			if (userPermission == null || userPermission.getFieldPermissionType() == null) {	// the saved value is null
				filePermissionType = permission.getEnableFieldPermissionType();
			} else {
				filePermissionType = userPermission.getFieldPermissionType();
			}
			permission.setFieldPermissionType(filePermissionType);
			if (userPermission != null) {
				permission.setScopeType(userPermission.getScopeType());
				permission.setWriteScopeType(userPermission.getWriteScopeType());
			}
			fieldPermissions.add(permission);
		}
	}
	
	/**
	 * 
	 * the result 'GroupPrivilege' is only used by the dropdownlist, this display fields comes from fieldPermissions
	 * 
	 * @param category
	 * @param model
	 * @param method
	 * @param fieldPermissions
	 * @param userId
	 * @param modelPrivilege
	 * @param websiteId
	 * @return
	 */
	@Rest(simpleServiceName="espirit.eap.sa.user", type="add")
	public void showFieldPrivilege(String category, String model, String method, String userId, Integer websiteId, boolean modelPrivilege,
			List<KeyValue> categoryOptions, List<KeyValue> modelOptions, List<KeyValue> methodOptions, List<FieldPermission> fieldPermissions) {
		if (modelPrivilege) {
			showModelFieldPrivilege(category, model, userId, websiteId, categoryOptions, modelOptions, fieldPermissions);
			return;
		}
		
		GroupPrivilege groupPrivilege = getGroupPrivilege(userId);
		if (category == null) {
			Collection<String> categories = groupPrivilege.getCategories();
			if (categories.size() > 0) {
				category = categories.iterator().next();
				ActionContext.getContext().getValueStack().setValue("category", category);
			}
		}
		if (model == null) {
			Collection<String> models = groupPrivilege.getModels(category);
			if (models.size() > 0) {
				model = models.iterator().next();
				ActionContext.getContext().getValueStack().setValue("model", model);
			}
		}
		if (method == null) {
			Collection<String> methods = groupPrivilege.getMethod(category, model);
			if (methods.size() > 0) {
				method = methods.iterator().next();
				ActionContext.getContext().getValueStack().setValue("method", method);
			}
		}
		
		for (String categoryData : groupPrivilege.categories) {
			String i18nName = i18nService.getI18n("module." + categoryData).getName();
			categoryOptions.add(new KeyValue(categoryData, i18nName));
		}
		modelOptions.addAll(groupPrivilege.getModelOptions(category));
		methodOptions.addAll(groupPrivilege.getMethodOptions(category, model));
		
		// load user's field permission
		Map<String, FieldPermission> userFieldPermission = new HashMap<String, FieldPermission>();
		QLInfo qlInfo;
		String ql = "from " + UserTemplatePrivilege.class.getName() + " a where a.userTemplate.id=?"
				+ " and (a.privilege.modelSimpleName=? or a.privilege.mappingModelSimpleName=?)";
		if (method.startsWith(FrameConstant.ACTION_PREPARE)) {
			ql += " and a.privilege.method=?";
			qlInfo = new QLInfo(ql, true, userId, model, model, method);
		} else {
			String prepareMethod = FrameConstant.ACTION_PREPARE + method;
			ql += " and (a.privilege.method=? or a.privilege.method=?) order by a.privilege.method desc";
			qlInfo = new QLInfo(ql, true, userId, model, model, method, prepareMethod);
		}
		List<UserTemplatePrivilege> userTemplatePrivileges = genericService.searchByQl(qlInfo, false).getData();
		
		for (UserTemplatePrivilege userTemplatePrivilege : userTemplatePrivileges) {
			List<PageFieldPermission> templatePrivilegeFieldPermissions = userTemplatePrivilege.getPageFieldPermissions();
			for (FieldPermission fieldPermission : templatePrivilegeFieldPermissions) {
				userFieldPermission.put(fieldPermission.getFieldName(), fieldPermission);
			}
		}
				
		// get all the mapping method according the page fields
		Collection<String> methodMapping = groupPrivilege.getMethodMapping(model, method);
		Set<String> dupliCheck = new HashSet<String>();
		for (String theMethod : methodMapping) {
			// FIXME: Hardcode logic, don't show the view page
			if (theMethod.equals("view")) {
				continue;
			}
			
			String mappingModelName = groupPrivilege.getMappingModelName(model);
			PageElements pageElements = pageElementService.getPageElementsNoPermissionFilter(mappingModelName, theMethod);
			if (pageElements == null) continue;
			// TODO: remove it, it's legacy code
			if (pageElements.getAllElements().isEmpty() 
					&& !pageConverterService.hasPage(mappingModelName, theMethod)) {
//				pageElements = tryLoadTplPage(mappingModelName, theMethod);
			}
			if (pageElements == null) continue;
			for (PageElement pageElement : pageElements.getAllElements().values()) {
				if (!dupliCheck.add(pageElement.getName())) {	// skip the existed element
					continue;
				}
				
				if (pageElement.getLabel() != null && !pageElement.getLabel().isEmpty() 
						&& (pageElement.getHidden() == null || !pageElement.getHidden())) {
					String fieldName = pageElement.getName();
					
					FieldPermission permission = new FieldPermission();
					permission.setFieldName(fieldName);
					// name starts with: model, advanceModel, execute
					permission.setFieldNameLabel(pageElement.getLabel());
					
					String tagName = pageElement.getType();
					boolean isModelFieldTag = tagManager.isModelFieldTag(tagName);
					if (!isModelFieldTag || pageElement.getTextModel() != null && pageElement.getTextModel()) {
						permission.setEnableFieldPermissionType(FieldPermissionType.READ);
					} else {
						// it's editable model field
						permission.setEnableFieldPermissionType(FieldPermissionType.READWRITE);
					}
					
					// set the user's field permission according to the DB record
					FieldPermission userPermission = userFieldPermission.get(permission.getFieldName());
					FieldPermissionType filePermissionType; 
					if (userPermission == null || userPermission.getFieldPermissionType() == null) {	// the saved value is null
						filePermissionType = permission.getEnableFieldPermissionType();
					} else {
						filePermissionType = userPermission.getFieldPermissionType();
					}
					permission.setFieldPermissionType(filePermissionType);
					if (userPermission != null) {
						permission.setScopeType(userPermission.getScopeType());
						permission.setWriteScopeType(userPermission.getWriteScopeType());
					}
					// TODO: support the scope condition: clone & add more conditions
					fieldPermissions.add(permission);
				}
			}
		}
		
		Collections.sort(fieldPermissions, new Comparator<FieldPermission>() {
			@Override
			public int compare(FieldPermission o1, FieldPermission o2) {
				String category1 = getCategory(o1.getFieldName());
				String category2 = getCategory(o2.getFieldName());
				if (category1.equals(category2)) {
					return 0;
				} else {
					int order1 = getOrder(category1);
					int order2 = getOrder(category2);
					return order1 - order2;
				}
			}
			
			private String getCategory(String fieldName) {
				int categoryIndex = fieldName.indexOf('.');
				return categoryIndex > 0 ? fieldName.substring(0, categoryIndex) : fieldName;
			}
			
			private int getOrder(String category) {
				// execute, action, model, advanceModel, the others  
				if ("execute".equals(category)) {
					return 1;
				} else if ("action".equals(category)) {
					return 2;
				} else if ("model".equals(category)) {
					return 3;
				} else if ("advanceModel".equals(category)) {
					return 4;
				} else {
					return 5;
				}
			}
		});
	}
	
//	private PageElements tryLoadTplPage(String modelName, String method) {
//		LinkedModel linkedModel = new LinkedModel();
//		String simpleModelName = beanIntrospector.getSimpleName(modelName);
//		linkedModel.setModel(simpleModelName);
//		linkedModel.setMethod(method);
//		LinkedModel dbModel = genericService.searchAll(linkedModel).getData(0);
//		
//		if (dbModel != null) {
//			Element layout = dbModel.getElement();
//			
//			if (layout != null) {
//				PageElements pageElements = pageElementManager.getPageElementsNoPermissionFilter(
//						modelName, method);
//				return pageElements;
//			}
//		}
//		return null;
//	}


	/**
	 * 
	 * @param templateId the loaded User or Template
	 * @param tabId the target tab id to show the privilege
	 * @param selectedPrivileges
	 * @return
	 */
	@Rest(simpleServiceName="espirit.eap.sa.user", type="add")
	public GroupPrivilege show(String templateId, String tabId, Integer websiteId,
			@ParamScope(value="#fn.newModel('java.util.ArrayList')")
			List<String> selectedPrivileges) {
		String productName;
		if (websiteId == null) {
			productName = ContextHolder.get().getProductName();
		} else {
			productName = genericService.load(Website.class, websiteId).getName();
		}
		GroupPrivilege groupPrivilege = getGroupPrivilegeByProductName(productName);
		selectedPrivileges.addAll(loadSelectedPrivileges(templateId));
		return groupPrivilege;
	}
	
	public GroupPrivilege getGroupPrivilege(String userId) {
		GroupPrivilege groupPrivilege;
		if (userId == null) {
			// TODO: think about it, use the default site or current site?
			String productName = ContextHolder.get().getProductName();
			groupPrivilege = getGroupPrivilegeByProductName(productName);
		} else {
			UserTemplate user = genericService.load(UserTemplate.class, userId);
			List<UserTemplatePrivilege> userTemplatePrivileges = user.getUserTemplatePrivileges();
			List<Privilege> privileges = new ArrayList<Privilege>();
			if (userTemplatePrivileges != null) {
				for (UserTemplatePrivilege userTemplatePrivilege : userTemplatePrivileges) {
					privileges.add(userTemplatePrivilege.getPrivilege());
				}
			}
			groupPrivilege = buildGroupPrivilege(privileges);
		}
		return groupPrivilege;
	}
	
	// read the privilege information and save it to GroupPrivilege
	private GroupPrivilege getGroupPrivilegeByProductName(String productName) {
		// TODO: support the product? necessary?
		GroupPrivilege groupPrivilege = groupPrivileges.get(productName);
		
		if (groupPrivilege == null) {
			String ql = "from " + Privilege.class.getName() + " a";
			QLInfo qlInfo = new QLInfo(ql, true);
			PageInfo<Privilege> privileges = (PageInfo<Privilege>)genericService.searchByQl(qlInfo, false);
			groupPrivilege = buildGroupPrivilege(privileges.getData());
			groupPrivileges.put(productName, groupPrivilege);
		}
		return groupPrivilege;
	}
	
	@Protected
	public void resetPrivilege() {
		this.groupPrivileges.clear();
	}
	
	public void addPrivilegeToUser(Privilege privilege, UserTemplate user) {
		UserTemplate dbUser = genericService.load(UserTemplate.class, user.getId());
		List<UserTemplatePrivilege> pagePermissions = dbUser.getUserTemplatePrivileges();
		if (pagePermissions == null) return ;
		for (UserTemplatePrivilege pagePermission : pagePermissions) {
			Integer privilegeId = pagePermission.getPrivilege().getId();
			if (privilegeId.equals(privilege.getId())) {
				// page permission exists
				return ;
			}
		}
		
		// add new permission
		UserTemplatePrivilege userTemplatePrivilege = new UserTemplatePrivilege();
		userTemplatePrivilege.setUserTemplate(user);
		Privilege privilegeModel = genericService.load(privilege);
		userTemplatePrivilege.setPrivilege(privilegeModel);
		pagePermissions.add(userTemplatePrivilege);
		genericService.merge(dbUser);
	}
	
	public void addPrivilege(String path) {
		String [] notWritePaths = new String[]{ "/index.execute.html"
												, "/index.admin.html"
												,"/loginUser.free.html"
												, "/espirit/eap/pagelayout/page._importLayout.html"
												, "/espirit/eap/pagelayout/page.exportLayout.html"
												, "/espirit/eap/pagelayout/page.viewImageFile.html"};
		for(String notWritePath : notWritePaths){
			if (notWritePath.equals(path)){
				return ;
			}
		}
		Privilege privilege = new Privilege();
		Invoker invoker = routing.path2Invoker(path, false);
		String simpleModelName = beanIntrospector.getSimpleName(invoker.actionName);
		privilege.setModelSimpleName(simpleModelName);
		String methodName = invoker.methodName;
		if (!"execute".equals(methodName)) {
			String serviceName = beanIntrospector.getServiceName(simpleModelName);
			Method method = ClassUtil.getServiceMethod(serviceName, methodName);
			if (method == null) {
				String genericServiceName = ClassUtil.getClassName(genericService);
				method = ClassUtil.getServiceMethod(genericServiceName, methodName);
			}
			
			if (method != null) {
				// Use the mapping service name and method name
				Rest rest = method.getAnnotation(Rest.class);
				if (rest != null) {
					methodName = rest.type();
					
					if (rest.simpleServiceName() != null && !rest.simpleServiceName().isEmpty()) {
						String mappingServiceName = rest.simpleServiceName();
						simpleModelName = beanIntrospector.getSimpleName(mappingServiceName);
						
						if ("loginUser".equals(simpleModelName) && "free".equals(methodName) ){
							return;
						}
						
						privilege.setModelSimpleName(simpleModelName);
					}
				}
			}			
		}
		privilege.setMethod(methodName);
		Privilege existedPrivilege = genericService.searchAll(privilege).getData(0);
		if (existedPrivilege == null) {
			privilege.setScopeType(ScopeType.Global);
			privilege = genericService.add(privilege);
			
			// add privilege to admin user automatically
			LoginUser userAdmin = new LoginUser();
			userAdmin.setName(LoginUser.SUPER_ADMIN);
			userAdmin = genericService.searchAll(userAdmin).getData(0);
			if (userAdmin != null) {
				addPrivilegeToUser(privilege, userAdmin);
				for (String thePath : toPathsWithSite(privilege)) {
					safeManager.addPagePermission(userAdmin.getId().toString(), thePath);
				}
			}
			
			resetPrivilege();
		}
	}
	
	private GroupPrivilege buildGroupPrivilege(List<Privilege> privileges) {
		Map<String, CategoryModel> group = new LinkedHashMap<String, CategoryModel>();
		Map<String, CategoryModel> others = new LinkedHashMap<String, CategoryModel>();
		for (Privilege privilege : privileges) {
			String mappingModelName = getMappingModelSimpleName(privilege);
			if (mappingModelName != null) {
				String category = privilege.getCategory();
				String method = methodMapping.get(privilege.getMethod());
				
				Map<String, CategoryModel> theGroup;
				if (method == null ) {	
					method = othersMethodMapping.get(privilege.getMethod());
					if (method == null) {
						method = privilege.getMethod();
					}
					theGroup = others;
				} else {
					theGroup = group;
				}
				
				// group the privilege according to: category, model
				CategoryModel categoryModel = theGroup.get(category);
				if (categoryModel == null) {
					categoryModel = new CategoryModel(category);
					theGroup.put(category, categoryModel);
				}
				ModelMethod modelMethod = categoryModel.models.get(mappingModelName);
				if (modelMethod == null) {
					modelMethod = new ModelMethod(privilege.getModelSimpleName(), mappingModelName);
					categoryModel.models.put(mappingModelName, modelMethod);
				}
				// and then group according to the method
				addMethodPrivilege(modelMethod, method, privilege);
			}
		}
		return new GroupPrivilege(group, others);
	}
	
	private void addMethodPrivilege(ModelMethod modelMethod, String method, Privilege value) {
		MethodPrivilege methodPrivilege = modelMethod.methods.get(method);
		if (methodPrivilege == null) {
			methodPrivilege = new MethodPrivilege(method);
			modelMethod.methods.put(method, methodPrivilege);
		}
		
		// add by scope order
		int index = 0;
		int curScope = toInt(value);
		List<Privilege> privileges = methodPrivilege.privileges;
		for (int len = privileges.size(); index < len; index++) {
			Privilege privilege = privileges.get(index);
			if (curScope <= toInt(privilege)) {
				break;
			}
		}
		privileges.add(index, value);
	}
	
	private int toInt(Privilege privilege) {
		if (privilege.getScopeType() == null) {
			return ScopeType.Basic.toInt();
		}
		return privilege.getScopeType().toInt();
	}
	
	private class CategoryModel {
		String category;
		Map<String, ModelMethod> models;
		
		public CategoryModel(String category) {
			this.category = category;
			this.models = new LinkedHashMap<String, ModelMethod>();
		}
		
		public Collection<String> getModelList() {
			return models.keySet();
		}
	}
	
	private class ModelMethod {
		String mappingModel;
		String model;
		Map<String, MethodPrivilege> methods;
		
		public ModelMethod(String model, String mappingModel) {
			this.model = model;
			this.mappingModel = mappingModel;
			this.methods = new LinkedHashMap<String, MethodPrivilege>();
		}
		
		public Collection<String> getMethodList() {
			return methods.keySet();
		}
	}
	
	private class MethodPrivilege {
		String method;
		List<Privilege> privileges;
		
		public MethodPrivilege(String method) {
			this.method = method;
			this.privileges = new ArrayList<Privilege>();
		}
	}
	
	/**
	 * Notice: privilege is grouped by path, it maybe different with the menu tree
	 */
	public class GroupPrivilege {

		// grouped privilege data, category --> class --> method --> privilege
		private Map<String, CategoryModel> group;
		
		// grouped privilege data, category --> (class + method) --> List<privilege>
		private Map<String, CategoryModel> others;
		
		private List<String> categories;
		
		private List<String> commonMethods = Arrays.asList("add", "update", "delete", "search",  "export" ,"_importFile","report","getLayout");
		
		
		public GroupPrivilege(Map<String, CategoryModel> group, Map<String, CategoryModel> others) {
			this.group = group;
			this.others = others;
			
			categories = new ArrayList<String>();
			
			final Map<String, Integer> categoryOrders = new HashMap<String, Integer>();
			categoryOrders.put("common", 1);
			categoryOrders.put("sale", 2);
			categoryOrders.put("purchase", 3);
			categoryOrders.put("process", 4);
			categoryOrders.put("logistics", 5);
			categoryOrders.put("finance", 6);
			categoryOrders.put("stat", 7);
			categoryOrders.put("eap", 8);
			
			for (String category : group.keySet()) {
				if (!categories.contains(category)) {
					categories.add(category);
				}
			}
			for (String category : others.keySet()) {
				if (!categories.contains(category)) {
					categories.add(category);
				}
			}
			
			Collections.sort(categories, new Comparator<String>() {
				@Override
				public int compare(String c1, String c2) {
					Integer o1 = categoryOrders.get(c1);
					if (o1 == null) {
						o1 = 9;
					}
					Integer o2 = categoryOrders.get(c2);
					if (o2 == null) {
						o2 = 9;
					}
					return o1 - o2;
				}
			});
		}
		
		public String getMappingModelName(String model) {
			for (CategoryModel categoryModel : group.values()) {
				for (ModelMethod modelMethod : categoryModel.models.values()) {
					if (modelMethod.mappingModel.equals(model)) {
						if (modelMethod.model != null) {
							return modelMethod.model;
						} else {
							return model;
						}
					}
				}
			}
			return model;
		}

		public Collection<String> getAllCategories() {
			return categories;
		}
		
		public Collection<String> getCategories() {
			return group.keySet();
		}
		
		public Collection<KeyValue> getCategoryOptions() {
			Collection<String> categories = getCategories();
			List<KeyValue> list = new ArrayList<KeyValue>(categories.size());

			for (String category : categories) {
				String name = i18nService.getI18n("module." + category).getName();
				list.add(new KeyValue(category, name));
			}
			return list;
		}
		
		public Collection<String> getMethods() {
			return commonMethods;
		}

		public Collection<String> getMethod(String category, String model) {
			Set<String> methods = new LinkedHashSet<String>();
			
			for (Map<String, CategoryModel> theGroup : new Map[]{group, others}) {
				ModelMethod modelMethod;
				if (category == null && theGroup.size() > 0) {
					category = theGroup.keySet().iterator().next();
				}
				CategoryModel categoryModel = theGroup.get(category);
				if (categoryModel != null) {
					if (model == null && categoryModel.models.size() > 0) {
						model = categoryModel.models.keySet().iterator().next();
					}
					modelMethod = categoryModel.models.get(model);
					if (modelMethod != null) {
						for (String method : modelMethod.getMethodList()) {
							String mappingMethod;
							if (theGroup == group) {
								mappingMethod = methodMapping.get(method);
							} else {
								mappingMethod = othersMethodMapping.get(method);
							}
							
							if (mappingMethod != null) {
								methods.add(mappingMethod);
							} else {
								methods.add(method);
							}
						}
					}
				}
			}
			
			// Skip method: delete, getLayout, TODO: check each method and skip the method if there's not pageElement?
			methods.remove("delete");
			methods.remove("getLayout");
			methods.remove("export");
			return methods;
		}
		
		// multiple method mapped to one method, such as 'update' --> ['_update', 'update']
		public Collection<String> getMethodMapping(String model, String method) {
			Set<String> methodMapping = new LinkedHashSet<String>();
			
			for (Map<String, CategoryModel> theGroup : new Map[]{group, others}) {
				for (CategoryModel categoryModel : theGroup.values()) {
					ModelMethod modelMethod = categoryModel.models.get(model);
					if (modelMethod != null) {
						MethodPrivilege methodPrivilege = modelMethod.methods.get(method);
						if (methodPrivilege != null) {
							for (Privilege privilege : methodPrivilege.privileges) {
								methodMapping.add(privilege.getMethod());
							}
						}
					}
				}
			}
			
			return methodMapping;
		}
		
		public Collection<KeyValue> getMethodOptions(String category, String model) {
			Collection<String> methods = getMethod(category, model);
			List<KeyValue> list = new ArrayList<KeyValue>(methods.size());

			for (String method : methods) {
				String name = i18nService.getI18n("action." + method).getName();
				list.add(new KeyValue(method, name));
			}
			return list;
		}
		
		public Collection<String> getModels(String category) {
			if (category == null) {
				if (group.size() > 0) {
					category = group.keySet().iterator().next().toString();
				}
			}
			CategoryModel categoryModel = group.get(category);
			
			if (categoryModel != null) {
				return categoryModel.getModelList();
			} else {
				return Collections.EMPTY_LIST;
			}
		}
		
		public String getModelI18n(String model) {
			return modelService.getI18nValue(model);
		}
		
		/*
		 * show all models in the select option
		 */
		public Collection<KeyValue> getModelOptions(String category) {
			Collection<String> models = getModels(category);
			List<KeyValue> list = new ArrayList<KeyValue>(models.size());

			for (String model : models) {
				String name = modelService.getI18nValue(model);
				list.add(new KeyValue(model, name));
			}
			return list;
		}
		
		
		
		public Collection<String> getOthersKeys(String category) {
			CategoryModel categoryModel = others.get(category);
			if (categoryModel != null) {
				List<String> keys = new ArrayList<String>();
				for (ModelMethod item : categoryModel.models.values()) {
					String model = item.mappingModel;
					for (String method : item.getMethodList()) {
						if (method == null) {
							method = "execute";
						}
						String key = model + '.' + method;
						keys.add(key);
					}
				}
				return keys;
			} else {
				return Collections.EMPTY_LIST;
			}
		}
		
		
		
		@SuppressWarnings("unchecked")
		public Collection<Privilege> getPrivileges(String category, String model, String method) {
			MethodPrivilege methodPrivilege = group.get(category).models.get(model).methods.get(method);
			if (methodPrivilege == null) {
				return Collections.EMPTY_LIST;
			} else {
				return methodPrivilege.privileges;
			}
		}

		public String getPrivilegeFlags(String category, String model, String method) {
			Collection<Privilege> privileges = getPrivileges(category, model, method);
			return convertPrivilegeFlagToString(privileges);
		}
		
		public String getOtherPrivilegeFlags(String category, String key) {
			Collection<Privilege> privileges = getOtherPrivileges(category, key);
			return convertPrivilegeFlagToString(privileges);
		}
		
		// 将这一方法下的所有Privilege id按scopeType分组返回
		// 格式样本：id1,id2,id3:id4:id5,id6
		public String getPrivilegeIds(String category, String model, String method) {
			Collection<Privilege> privileges = getPrivileges(category, model, method);
			return convertPrivilegeIdToString(privileges);
		}
		
		public String getOtherPrivilegeIds(String category, String key) {
			Collection<Privilege> privileges = getOtherPrivileges(category, key);
			return convertPrivilegeIdToString(privileges);
		}
		
		public String getOtherPrivilegeI18n(String category, String key) {
			int index = key.lastIndexOf('.');
			String modelName = key.substring(0, index);
			String methodName = key.substring(index + 1);
			String modelI18nName = modelService.getI18nValue(modelName);
			
			Collection<Privilege> privileges = getOtherPrivileges(category, key);
			String privilegeI18nName = methodName;
			if (privileges.size() >= 1) {
				Privilege privilege = privileges.iterator().next();
				String i18n = privilege.getI18n();
				if (i18n == null) {
					i18n = modelService.getButtonI18nValue(modelName, methodName);
				}
				if (i18n != null && i18n.length() > 0) {
					privilegeI18nName = i18n;
				}
			}
			return modelI18nName + '-' + privilegeI18nName;
		}
		
		private Collection<Privilege> getOtherPrivileges(String category, String key) {
			int index = key.lastIndexOf('.');
			String model = key.substring(0, index);
			String method = key.substring(index + 1);
			Map<String, MethodPrivilege> methods = others.get(category).models.get(model).methods;
			MethodPrivilege methodPrivilege = methods.get(method);
			if (methodPrivilege == null && "execute".equals(method)) {
				methodPrivilege = methods.get(null);
			}
			if (methodPrivilege == null) {
				return Collections.EMPTY_LIST;
			} else {
				return methodPrivilege.privileges;
			}
		}
		
		// 去重转换，flag之间以,号连接，传入的privileges的size为0时，所转结果会被privilege.show.ftl的判断过滤掉 
		private String convertPrivilegeFlagToString(Collection<Privilege> privileges) {
			// 0 表示未选择的flag状态
			StringBuilder sb = new StringBuilder("0");
			int lastScopeType = -1;
			for (Privilege privilege : privileges) {
				int scopeType = toInt(privilege);
				// 因为是有序的，所以只用比较最后一个是否相同即可
				if (scopeType != lastScopeType) {
					sb.append(',').append(scopeType);
					lastScopeType = scopeType;
				}
			}
			if (sb.length() > 1) {
				return sb.toString();
			} else {
				return "";
			}
		}
		
		// 直接转换，id之间以:号连接
		private String convertPrivilegeIdToString(Collection<Privilege> privileges) {
			StringBuilder sb = new StringBuilder();
			
			// 组装privilege, 同一标准方法下会有多个ScopeType, 每个ScopeType又可能对应到多个真实的方法
			int lastScopeType = -1;
			for (Privilege privilege : privileges) {
				int scopeType = toInt(privilege);
				if (scopeType != lastScopeType) {
					lastScopeType = scopeType;
					sb.append(',');
				} else {
					sb.append(':');
				}
				sb.append(privilege.getId());
			}
			return sb.toString();
		}
	}
	

	// TODO: use Rest to replace this function?
	public List<String> toPathsWithSite(Privilege privilege) {
		List<String> pathList = new ArrayList<String>();
		// model name
		String methodName = privilege.getMethod();
		String path = functions.linkModelNoBase(privilege.getModelSimpleName(), methodName, true);
		pathList.add(path);
		if (privilege.getMethod() != null && privilege.getMethod().startsWith(FrameConstant.ACTION_PREPARE)) {
			methodName = privilege.getMethod().substring(1);
			path = functions.linkModelNoBase(privilege.getModelSimpleName(), methodName, true);
			pathList.add(path);
		}
		
		// mapping model name
		if (privilege.getMappingModelSimpleName() != null && !privilege.getMappingModelSimpleName()
					.equals(privilege.getModelSimpleName())) {
			path = functions.linkModelNoBase(privilege.getMappingModelSimpleName(), privilege.getMethod(), true);
			pathList.add(path);
			if (privilege.getMethod().startsWith(FrameConstant.ACTION_PREPARE)) {
				path = functions.linkModelNoBase(privilege.getMappingModelSimpleName(), privilege.getMethod().substring(1), true);
				pathList.add(path);
			}
		}
		return pathList;
	}	
	
	@Transactional
	public void deleteList(List<Privilege> results) {
		for(Privilege privilege : results){
			if (privilege == null || privilege.getId() == null) continue;
			UserTemplatePrivilege utp = new UserTemplatePrivilege();
			utp.setPrivilege(privilege);
			List<UserTemplatePrivilege> foo = genericService.searchAll(utp).getData();
			for(UserTemplatePrivilege utps : foo){
				genericService.delete(utps);
			}
			genericService.delete(privilege);
		}
//		throw new ApplicationException("the privilege item cannt deleted:for it reserved!");
	}
	
	public Object _update(@GenericType Object model,@ParamScope(visible=false, value="#ps")PageElements ps) {
		for(PageElement pe : ps.getAllElements().values()){
			if (pe.getName().equals("model.condition")){
				ps.setElementReadOnly(pe.getName(), false);
			} else {
				ps.setElementReadOnly(pe.getName(), true);
			}
		}
		return genericService.view(model);
	}
	
	public void addMethodScopePrivile(String modelSimpleName,String method ,ScopeType scopeType, String i18nKey){
		String hql = "select a from " +  Privilege.class.getName() +  " a where a.modelSimpleName =? and a.method=? and a.scopeType=?";
		List<Object> params = new ArrayList<Object>();
		params.add(modelSimpleName);
		params.add(method);
		params.add(scopeType);
		
		if (genericService.searchByQl(new QLInfo(hql,true, params), false).getData(0) == null){
			Privilege privilege = new Privilege();
			privilege.setModelSimpleName(modelSimpleName);
			privilege.setMethod(method);
			privilege.setScopeType(scopeType);
			privilege.setI18n(i18nKey);
			privilege = genericService.add(privilege);
			logger.debug("insert privilege:" + modelSimpleName + "." + method);
		}
	}
	

	public void registerPrivileges(){
//		List<String> methods = Arrays.asList("_add", "_update", "deleteList", "search", "export");
		List<Model> results = modelService.getAllDomainModels();
		for(Model model : results){
			String modelName = model.getName();
			String fullModelName = beanIntrospector.getModelName(modelName);
			
			Class clazz = ClassUtil.getClass(fullModelName);
			if (clazz == null) continue;
			List<AvabButton> buttons = model.getAvabButtons();
			if (buttons == null) return ;
			
			Set<String> buttonNames = new HashSet<String>();
			String serviceName = beanIntrospector.getServiceName(beanIntrospector.getSimpleName(clazz.getName()));
			Object service = objectFactory.getBean(serviceName,false );
			
			for(AvabButton button: buttons){
				String methodName = button.getName();
				if (clazz.getName().equals(User.class.getName()) && "apply".equals(methodName)){
					continue;
				}
				if (service != null){
					Method method = getServiceMethod(serviceName, methodName);
					if (method != null) {
						Rest rest = method.getAnnotation(Rest.class);
						if (rest != null) {
							continue;
						}
					} else {
						if (button instanceof LinkedButton){
							continue;
						}
					}
				} else {
					if (button instanceof LinkedButton){
						continue;
					}
				}
				buttonNames.add(methodName);
			}
//			if (buttonNames.contains("_add") || buttonNames.contains("add")){
				buttonNames.add("_update");
//			}
//			List<String> scopeMethods = Arrays.asList("_search", "search", "_add", "add", "_update", "update", "deleteList", "delete", "export");
			List<String> scopeMethods = Arrays.asList("_search", "search");
			for(String method : buttonNames){
				addMethodGlobalPrivilege(modelName, method);
				if (scopeMethods.contains(method)){
					if (BasicScopeAware.class.isAssignableFrom(clazz)){
						addMethodScopePrivile(modelName, method, ScopeType.Basic);
					}
					if (LocalScopeAware.class.isAssignableFrom(clazz)){
						addMethodScopePrivile(modelName, method, ScopeType.Local);
						addMethodScopePrivile(modelName, method, ScopeType.Deep);
					}
				}
			}
			if (model.getDefaultListLayout() != null && model.getDefaultListLayout().getMainGridSection() != null){
				Map<String, String> properties = model.getDefaultListLayout().getMainGridSection().getExProperties();
				if (properties != null){
					String clickLinks = properties.get("rowClickLinks");
					if (clickLinks != null){
						//getlayout privilege
						addMethodScopePrivile(modelName, "getLayout", ScopeType.Global);
					}
				}
			}
		}
	}
	
	private Method getServiceMethod(String serviceName, String methodName) {
		Class serviceClazz;
		try {
			serviceClazz = Class.forName(serviceName, true, Thread.currentThread().getContextClassLoader());
			Method[] methods = serviceClazz.getMethods();
			for (Method method : methods) {
				if (method.getName().equals(methodName)
						&& method.getAnnotation(Protected.class) == null) {
					return method;
				}
			}
		} catch (Throwable e) {
			if (logger.isDebugEnabled()) {
				logger.debug("Skip check the Rest permission, no mapping class found:" + serviceName + '#' + methodName);
			}
		}
		return null;
	}
	
	private void addMethodGlobalPrivilege(String modelName,String method){
		addMethodScopePrivile(modelName, method, ScopeType.Global);
	}
	
	public void addMethodScopePrivile(String modelSimpleName,String method ,ScopeType scopeType){
		addMethodScopePrivile(modelSimpleName, method, scopeType, null);
	}

}